package ancun;

import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.*;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.cert.X509Certificate;
import java.util.*;

import com.itextpdf.text.pdf.security.PdfPKCS7;


import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import com.itextpdf.text.pdf.security.TSAClient;
import com.itextpdf.text.pdf.security.TSAClientBouncyCastle;

import java.io.*;
import java.security.MessageDigest;
import java.security.SignatureException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.HashMap;

import com.itextpdf.text.pdf.security.MakeSignature.CryptoStandard;

import javax.xml.bind.DatatypeConverter;

/**
 * Created by zhangzhenhua on 2016/11/1.
 */
public class PDFSigner {

    //tsa

    private SignerKeystore signerKeystore;
    private TSAClient tsaClient;

//    private PDFSigner(){}

    /**
     *
     * @param tsa_url   tsa服务器地址
     * @param tsa_accnt tsa账户号
     * @param tsa_passw tsa密码
     * @param cert_path 证书路径
     * @param cert_passw    证书密码
     */
    public PDFSigner(String tsa_url,String tsa_accnt,String tsa_passw,String cert_path,String cert_passw)  {

        tsaClient = new TSAClientBouncyCastle(tsa_url, tsa_accnt, tsa_passw);
        try {
            signerKeystore =  new SignerKeystorePKCS12(new FileInputStream(cert_path), cert_passw);

            FileInputStream fin = new FileInputStream("D://my_rsa_sign.txt");
            int fileSize = fin.available();
            System.out.println("File size: " + fileSize);
            byte[] digest = new byte[fileSize];
            fin.read(digest, 0, fileSize);

            long time1 = System.currentTimeMillis();
            byte[] tsImprint = tsaClient.getMessageDigest().digest(digest);
            String tsImprotStr = DatatypeConverter.printHexBinary(tsImprint);
            long time2 = System.currentTimeMillis();
            System.out.printf("diges time: %d, idigest hex: %s\n", time2 - time1, tsImprotStr);

            byte[] tsToken = tsaClient.getTimeStampToken(tsImprint);
            long time3 = System.currentTimeMillis();
            System.out.printf("Get timestamp token time: %d\n", time3 - time2);
            FileOutputStream fout = new FileOutputStream("D://hancm_test.ts");
            fout.write(tsToken);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * TSA时间戳签名
     * @param infilePath    未签名的文件路径
     * @param outfilePath   签名后的文件路径
     * @throws Exception
     */
    public void signPDF(String infilePath,String outfilePath) throws Exception {
        PdfReader reader = new PdfReader(infilePath);
        FileOutputStream fout = new FileOutputStream(outfilePath);

        PdfStamper stp = PdfStamper.createSignature(reader, fout, '\0', null, true);
        PdfSignatureAppearance sap = stp.getSignatureAppearance();

        //////////////
        //sap.setCrypto(null,  this.signerKeystore.getChain(), null, PdfSignatureAppearance.SELF_SIGNED);

        sap.setVisibleSignature(new Rectangle(0, 0, 115, 114), 1, "Signature");

        PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, new PdfName("adbe.pkcs7.detached"));
        dic.setReason(sap.getReason());
        dic.setLocation(sap.getLocation());
        dic.setContact(sap.getContact());
        dic.setDate(new PdfDate(sap.getSignDate()));
        sap.setCryptoDictionary(dic);

        int contentEstimated = 15000;
        HashMap exc = new HashMap();
        exc.put(PdfName.CONTENTS, new Integer(contentEstimated * 2 + 2));
        sap.preClose(exc);

        PdfPKCS7 pdfPkcs7 = new PdfPKCS7(this.signerKeystore.getPrivateKey(),  this.signerKeystore.getChain(),
                "SHA1", null, null, false);

        // range范围的sha1值, 包含/Contents
        InputStream data = sap.getRangeStream();
        MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
        byte buf[] = new byte[8192];
        int n;
        while ((n = data.read(buf)) > 0) {
//            String decodedString = new String(buf, 0, n, "UTF-8");
//            System.out.println(decodedString);
            messageDigest.update(buf, 0, n);
        }

        // range范围的sha1值, 包含/Contents
        byte hash[] = messageDigest.digest();
        System.out.println("################Sha1: ####################");
        String hashStr = DatatypeConverter.printHexBinary(hash);
        System.out.printf("Hash hex: %s\n", hashStr);
//        System.out.println(new String(hash, "UTF-8"));

        //Calendar cal = Calendar.getInstance();
        //byte[] ocsp = null;
//        if ( this.signerKeystore.getChain().length >= 2) {
//            String url = PdfPKCS7.getOCSPURL((X509Certificate) this.signerKeystore.getChain()[0]);
//            if (url != null && url.length() > 0)
//                ocsp = new OcspClientBouncyCastle((X509Certificate) this. signerKeystore.getChain()[0],
//                        (X509Certificate) this.signerKeystore.getChain()[1], url).getEncoded();
//        }

        // 1.2.840.113549.1.9.3contentType(PKCS #9)
        // 1.2.840.113549.1.7.1data(PKCS #7)
        // 1.2.840.113549.1.9.4messageDigest(PKCS #9)
        // 53848738C9F93CCF2D86968BD7E07B7FB4FE458B ---- range sha1
        byte sh[] = pdfPkcs7.getAuthenticatedAttributeBytes(hash, null, null, null);
        System.out.println("#########Authenticated attr : ###################");
        String authenAttr = DatatypeConverter.printHexBinary(sh);
        System.out.printf("Authenticated hex: %s\n", authenAttr);
        pdfPkcs7.update(sh, 0, sh.length);

        byte[] encodedSig = pdfPkcs7.getEncodedPKCS7(hash, tsaClient, (byte[])null, null, MakeSignature.CryptoStandard.CMS);
        System.out.println("####encode sig : #######################");
        String helloHex = DatatypeConverter.printHexBinary(encodedSig);
        System.out.printf("Hello hex: %s\n", helloHex);
//        System.out.println(Arrays.toString(encodedSig));

        if (contentEstimated + 2 < encodedSig.length)
            throw new Exception("Not enough space");

        byte[] paddedSig = new byte[contentEstimated];
        System.arraycopy(encodedSig, 0, paddedSig, 0, encodedSig.length);

        PdfDictionary dic2 = new PdfDictionary();
        dic2.put(PdfName.CONTENTS, new PdfString(paddedSig).setHexWriting(true));
        sap.close(dic2);
    }
}
